/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2021.
 * Description: self-decompressed vxboot file
 * Author: yanbo <joey.yanbo@huawei.com>
 * Create: 2020-11-19
 */
#include <linux/compiler.h>	/* for inline */
#include <linux/types.h>	/* for size_t */
#include <linux/stddef.h>	/* for NULL */
#include <linux/linkage.h>
#include <linux/hal/watchdog.h>
#include <asm/string.h>

#include <asm/unaligned.h>
#include <mach/uncompress.h>
#include "string.h"

unsigned int rootfs_size;
/* must conform to bind tool & dtc's compile options */
#define DTB_SIZE        (0x4000)
#define REALLOC_ROOTFS_ADDR     (0x83000000)
#define ROOTFS_SIZE_MAX (32 * 1024 * 1024)
#define SPLIT_CPY_SIZE  (10 * 1024 * 1024)

#ifdef CONFIG_RTOS_HAL_DOUBLE_CLUSTER
/* the address to load uncompressed rootfs */
#define CLUSTER1_REALLOC_ROOTFS_ADDR (0xc3000000)
#endif

int is_vxboot_image(void)
{
	/* last 12 bytes' layout: */
	/* 4 bytes rootfs size big endian */
	/* 2 bytes magic # named 0xa5 0xa5 */
	/* 6 bytes string named "vxboot" */
	unsigned char *vxboot_sign = NULL;
	char str[8];

	if (!vx_edata)
		return 0;

	vxboot_sign = (unsigned char *)(vx_edata + DTB_SIZE - 4 - 2 - 6);
	if (vxboot_sign[4] != 0xa5 || vxboot_sign[5] != 0xa5)
		return 0;

	memcpy(str, vxboot_sign + 6, 6);
	str[6] = 0;
	if (memcmp(str, "vxboot", sizeof("vxboot")) == 0) {
		rootfs_size = (vxboot_sign[0] << 24) | (vxboot_sign[1] << 16) |
			(vxboot_sign[2] << 8) | vxboot_sign[3];
		if (rootfs_size > ROOTFS_SIZE_MAX) {
			hisi_putstr("rootfs too huge, need chk\n");
			print_number(rootfs_size);
			return 0;
		}
		hisi_putstr("vxboot image\n");
		memset(vxboot_sign, 0, 12);
		return 1;
	}
	return 0;
}


static void set_rootfs_realloc_addr(char **dst)
{
	int node;
	int addr;
	const u32 *data;
	void *dtb_address = (void *)__dtb_addr;

	node = fdt_node_offset_by_prop_value(dtb_address, -1,
			"compatible", "arm,cortex-a9-rootfs", sizeof("arm,cortex-a9-rootfs"));
	if (node >= 0) {
		data = fdt_getprop(dtb_address, node, "realloc-rootfs-addr", &addr);
		if (data)
			*dst = (char *)fdt32_to_cpu(*data);
	}
}

#ifdef CONFIG_RTOS_HAL_DOUBLE_CLUSTER
static int get_cluster_id(void)
{
	int node;
	int size;
	const u32 *data;
	void *dtb_address = (void *)__dtb_addr;

	node = fdt_node_offset_by_prop_value(dtb_address, -1,
			"compatible", "cluster_info", sizeof("cluster_info"));
	if (node >= 0) {
		data = fdt_getprop(dtb_address, node, "cluster_id", &size);
		if (data)
			return fdt32_to_cpu(*data);
	}
	return 0;
}
#endif

int mv_rootfs(void)
{
	int size = rootfs_size;
	char *dst = (char *)REALLOC_ROOTFS_ADDR;
	char *src = (char *)(vx_edata + DTB_SIZE);

	hisi_putstr("cpy rootfs\n");

	set_rootfs_realloc_addr(&dst);

#ifdef CONFIG_RTOS_HAL_DOUBLE_CLUSTER
	if (get_cluster_id() == 1)
		dst = (char *)CLUSTER1_REALLOC_ROOTFS_ADDR;
#endif
	while (size > SPLIT_CPY_SIZE) {
		memcpy(dst, src, SPLIT_CPY_SIZE);
		dst += SPLIT_CPY_SIZE;
		src += SPLIT_CPY_SIZE;
		size -= SPLIT_CPY_SIZE;
		hwdog_feed();
	}
	memcpy(dst, src, size);
	hwdog_feed();
	return 0;
}
